package com.ohuang.demo.view

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.text.Editable
import android.text.Spannable
import android.text.Spanned
import android.text.TextPaint
import android.text.TextWatcher
import android.text.method.KeyListener
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.KeyEvent
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.ExtractedText
import android.view.inputmethod.ExtractedTextRequest
import android.view.inputmethod.InputConnection
import android.view.inputmethod.InputMethodManager
import androidx.core.view.inputmethod.EditorInfoCompat

class SimpleEditText : ViewGroup {
    private var mDM: DisplayMetrics? = null
    private val sizeRect = Rect()
    private var mPaint: TextPaint? = null
    var editableText: Editable? = null
    private val isTextMode = true
    private var mIMM: InputMethodManager? = null
    private var windowToken: IBinder? = null
    val CHANGE_WATCHER_PRIORITY = 100
    private var changeWatcher: TextChangeWatcher? = null

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    init {
        mDM = resources.displayMetrics
        isFocusable = true
        isFocusableInTouchMode = true
        isClickable = true
        setWillNotDraw(false)
        initPaint()
        changeWatcher = TextChangeWatcher()

        //使用Editable方便修改文本，不用Editable也行的，只不过我们需要处理很多逻辑 ，参考 SimpleComposeInputConnection#setComposingText
        editableText = Editable.Factory.getInstance().newEditable("")
        if (editableText is Spannable) {
            //这种方式可以提前通知Watcher
            editableText?.setSpan(
                changeWatcher, 0, editableText!!.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE
                        or (CHANGE_WATCHER_PRIORITY shl Spanned.SPAN_PRIORITY_SHIFT)
            )
        }
        setOnClickListener { toggleFocus(isFocused) }
    }

    private inner class TextChangeWatcher : TextWatcher {
        override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
            postInvalidate()
        }

        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
            postInvalidate()
        }

        override fun afterTextChanged(s: Editable) {
            postInvalidate()
        }
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        windowToken = getWindowToken()
        if (mIMM == null) {
            mIMM = context.getSystemService(
                Context.INPUT_METHOD_SERVICE
            ) as InputMethodManager
        }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        handleInputMethodLeak(mIMM)
        mIMM = null
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {}
    override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
        toggleFocus(gainFocus)
    }

    private fun toggleFocus(gainFocus: Boolean) {
        if (gainFocus) {
            mIMM?.showSoftInput(this, 0)
        } else {
            mIMM?.hideSoftInputFromWindow(windowToken, 0)
        }
    }

    private fun handleInputMethodLeak(inputMethodManager: InputMethodManager?) {
        //InputMethodManager 很容易泄露，这点一定要注意
        // 这里需要修复，不过这仅仅是个demo，不至于要写多好
    }

    private fun initPaint() {
        //否则提供给外部纹理绘制
        mPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
            isAntiAlias = true
            strokeWidth = dp2px(1F)
            strokeCap = Paint.Cap.ROUND
            style = Paint.Style.STROKE
            textSize = dp2px(14F)
        }

    }

    override fun setEnabled(enabled: Boolean) {
        super.setEnabled(enabled)
        if (!enabled) {
            mIMM?.hideSoftInputFromWindow(windowToken, 0)
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        var widthSize = MeasureSpec.getSize(widthMeasureSpec)
        if (widthMode != MeasureSpec.EXACTLY) {

            widthSize = mDM?.widthPixels?.div(2) ?: 0

        }
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        var heightSize = MeasureSpec.getSize(heightMeasureSpec)
        if (heightMode != MeasureSpec.EXACTLY) {
            heightSize = dp2px(60f).toInt()
        }
        setMeasuredDimension(widthSize, heightSize)
    }

    fun dp2px(dp: Float): Float {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        mPaint?.let { paint ->
            editableText?.let {
                var strokeWidth = paint.strokeWidth
                sizeRect.set(
                    strokeWidth.toInt(),
                    strokeWidth.toInt(),
                    (width - strokeWidth.toInt()),
                    (height - strokeWidth.toInt())
                )
                canvas.drawRect(sizeRect, mPaint!!)
                val style = paint.style
                mPaint?.style = Paint.Style.FILL
                canvas.drawText(
                    it,
                    0,
                    it.length,
                    (sizeRect.left + 10).toFloat(),
                    getTextPaintBaseline(mPaint) + sizeRect.centerY(),
                    paint
                )
                mPaint?.style = style
            }
        }

    }

    val keyListener: KeyListener?
        get() = null

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {

        //这里需要TextKeyListener.getInstance()来辅助中文文本删除，参考TextView
        return super.onKeyDown(keyCode, event)
    }

    fun notifyContentCaptureTextChanged() {}
    fun setExtracting(request: ExtractedTextRequest?) {}
    override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection? {
        if (isEnabled) {
            outAttrs.inputType = inputType
            if (isTextMode) {
                val bundle = Bundle()
                outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE
                outAttrs.privateImeOptions = "DONE"
                outAttrs.actionLabel = "DONE"
                outAttrs.actionId = EditorInfo.IME_ACTION_DONE
                outAttrs.extras = bundle
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    val configuration = context.resources.configuration
                    val locales = configuration.locales
                    outAttrs.hintLocales = locales
                }
            } else {
                outAttrs.imeOptions = EditorInfo.IME_NULL
            }
            outAttrs.imeOptions = outAttrs.imeOptions or EditorInfo.IME_FLAG_NAVIGATE_NEXT
            outAttrs.imeOptions = outAttrs.imeOptions or EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS
            if (outAttrs.imeOptions and EditorInfo.IME_MASK_ACTION
                == EditorInfo.IME_ACTION_UNSPECIFIED
            ) {
                if (outAttrs.imeOptions and EditorInfo.IME_FLAG_NAVIGATE_NEXT != 0) {
                    // An action has not been set, but the enter key will move to
                    // the next focus, so set the action to that.
                    outAttrs.imeOptions = outAttrs.imeOptions or EditorInfo.IME_ACTION_NEXT
                } else {
                    // An action has not been set, and there is no focus to move
                    // to, so let's just supply a "done" action.
                    outAttrs.imeOptions = outAttrs.imeOptions or EditorInfo.IME_ACTION_DONE
                }
                outAttrs.imeOptions = outAttrs.imeOptions or EditorInfo.IME_FLAG_NO_ENTER_ACTION
            }
            val ic: InputConnection = SimpleComposeInputConnection(this)
            outAttrs.initialSelStart = 0
            outAttrs.initialSelEnd = 0
            outAttrs.initialCapsMode = ic.getCursorCapsMode(inputType)
            EditorInfoCompat.setInitialSurroundingText(outAttrs, editableText!!)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                outAttrs.contentMimeTypes = receiveContentMimeTypes
            }
            return ic
        }
        return null
    }

    private val inputType: Int
        private get() = EditorInfo.TYPE_CLASS_TEXT

    fun extractText(request: ExtractedTextRequest?, res: ExtractedText): Boolean {
        res.text = editableText
        res.startOffset = 0
        res.partialEndOffset = editableText!!.length
        res.partialStartOffset = -1 // -1 means full text
        res.selectionStart = 0
        res.selectionEnd = editableText!!.length
        res.flags = ExtractedText.FLAG_SINGLE_LINE
        return true
    }

    fun onEditorAction(actionCode: Int) {}
    fun beginBatchEdit() {}
    fun endBatchEdit() {}

    companion object {
        fun getTextPaintBaseline(p: Paint?): Float {
            val fontMetrics = p!!.fontMetrics
            return (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent
        }
    }


}